#include "mainwindow.h"
#include "settingsdialog.h"
#include <QTimer>
#include <QMessageBox>
#include <qapplication.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qpainter.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_curve.h>
#include <qwt_scale_draw.h>
#include <qwt_scale_widget.h>
#include <qwt_legend.h>
#include <qwt_legend_item.h>
#include "cpuplot.h"
#include <QSpinBox>
#include <QTextCodec>
//#define random  //如果定义了random，则将绘制随机图形

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
myPortSettings=new PortSettings();
myPortName=new QString();
on_settingsBtn_clicked();// 打开串口设置对话框
setupUi(this);
vBox=NULL;
textEdit->setFont(QFont("Courier",14,75));
textEdit->setReadOnly(true);
closeComBtn->setEnabled(false); //开始“关闭串口”按钮不可用
sendMsgBtn->setEnabled(false); //开始“发送数据”按钮不可用
temp=new QByteArray; //存放串口读入的数据
//textEdit->setMaximumHeight(500);//最高5000像素
}

void MainWindow::readMyCom()     //读串口函数
{
    temp->clear();
    temp->append(myCom->readAll().toHex());
    QString s=*temp;
    if(temp->size()>=50) //数据包应该至少50字节长
    {
         if((*temp)[0]=='7'&&(*temp)[1]=='e')
         {
         emit popPacketData(temp);
         }

        for(int n=0;n<temp->size();n+=2)
        {
        s.insert(3*n/2," ");

        }
       textEdit->insertPlainText(s);
        textEdit->insertPlainText("\n");
    }

}


MainWindow::~MainWindow()
{
    //delete timer;
 delete myPortSettings;
 delete myPortName;
 delete temp;
 myPortSettings=NULL;myPortName=NULL;temp=NULL;
}

/****槽函数，将dialog中的参数传给主窗口****/
 void MainWindow::setPortSettings(const PortSettings& portSettings)
{
    *myPortSettings=portSettings;
}
 void MainWindow::setPortName(const QString& name)
{
    *myPortName=name;
}

 void MainWindow::minAxisScaleChanged(int newMin)
 {
     ymin=newMin;
     plot->setAxisScale(QwtPlot::yLeft,ymin,ymax);
 }

 void MainWindow::maxAxisScaleChanged(int newMax)
 {
     ymax=newMax;
     plot->setAxisScale(QwtPlot::yLeft,ymin,ymax);
 }


 /******打开串口***********************/
void MainWindow::on_openComBtn_clicked()
{
    myCom = new QextSerialPort(*myPortName,*myPortSettings,QextSerialPort::Polling);
    //定义串口对象，并传递参数，在构造函数里对其进行初始化
    myCom ->open(QIODevice::ReadWrite); //打开串口

#if defined(Q_WS_WIN)
 //QextSerialPort(*myPortName,*myPortSettings,QextSerialPort::Polling);
#endif
    timer =new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(readMyCom()));
    timer->start(400);//略小于数据的发送间隔（500ms)

    if(myCom->isOpen())  //如果打开成功
    {
        textEdit->insertPlainText("This com is opened successfully");
        textEdit->insertPlainText("\n");

        openComBtn->setEnabled(false); //打开串口后“打开串口”按钮不可用
        closeComBtn->setEnabled(true); //打开串口后“关闭串口”按钮可用
        sendMsgBtn->setEnabled(true); //打开串口后“发送数据”按钮可用
    }
    else  //如果打开失败,显示错误信息
    {   textEdit->insertPlainText("This com hasn't been opend");
        textEdit->insertPlainText("\n");
         textEdit->insertPlainText(myCom->errorString());
         textEdit->insertPlainText("\n");
    }


}

/***** "关闭串口" 按钮的事件函数**********************/
void MainWindow::on_closeComBtn_clicked()
{
    myCom->close();     //关闭串口，该函数在win_qextserialport.cpp文件中定义
    openComBtn->setEnabled(true); //关闭串口后“打开串口”按钮可用
    closeComBtn->setEnabled(false); //关闭串口后“关闭串口”按钮不可用
    sendMsgBtn->setEnabled(false); //关闭串口后“发送数据”按钮不可用
    textEdit->insertPlainText("This com has been closed succesfully");
    textEdit->insertPlainText("\n");
    disconnect(timer, SIGNAL(timeout()), this, SLOT(readMyCom()));
    delete timer;
    timer=NULL;
    delete myCom;
    myCom=NULL;
}

/******************* "发送"按钮  *******************/
void MainWindow::on_sendMsgBtn_clicked()
{
    myCom->write(sendMsgEditLine->text().toAscii());
}

/******************* "设置"按钮  *******************/
void MainWindow::on_settingsBtn_clicked()
{
    Dialog=new settingsDialog(this);  //以当前主窗口为父对象
    if(Dialog)  //如果内存申请成功
    {
     connect(Dialog,SIGNAL(signalPortSettings(const PortSettings &)),
      this,SLOT(setPortSettings(const PortSettings&)) );//将对话框中的设置传给主窗口
     connect(Dialog,SIGNAL(signalMyComName(const QString&)),
      this,SLOT(setPortName(const QString&)) );//将对话框中的设置传给主窗口
    Dialog->setAttribute(Qt::WA_DeleteOnClose); //一旦关闭则删除
    Dialog->activateWindow();Dialog->raise();
    Dialog->exec();  //将对话框设置为模态，防止打开对话框的同时又按下“设置”键
    }
}

void MainWindow::on_clearBtn_clicked()
{
    textEdit->clear();
}

void MainWindow::on_plotBtn_clicked()
{
    /*************************下面为画图***************/
        plotBtn->hide();
        vBox=new QDialog(this);
        vBox->setWindowTitle("Plot");
        vBox->setWindowIcon(QIcon(":/curve/chart_curve_edit.png"));
        vBox->setAttribute(Qt::WA_DeleteOnClose);
        vBox->setWindowFlags(Qt::Window);

        QLabel *labelMin = new QLabel("Set the scale: Ymin:");
        labelMin->setFont(QFont("Courier",16,80));
        QLabel *labelMax = new QLabel(" Ymax:");
        labelMax->setFont(QFont("Courier",16,80));
        QSpinBox *minSpinBox=new QSpinBox();
        minSpinBox->setRange(-30,100);
        minSpinBox->setValue(0);
        minSpinBox->setSingleStep(10); minSpinBox->setFont(QFont("Courier",16,80));
        QSpinBox *maxSpinBox=new QSpinBox();
        maxSpinBox->setRange(-30,100);
        maxSpinBox->setValue(100);
        maxSpinBox->setSingleStep(10);maxSpinBox->setFont(QFont("Courier",16,80));
        ymin=-20;ymax=100;
        plot = new CpuPlot(&(*vBox));
        plot->setTitle("History");
        plot->setMargin(5);
        connect(this,SIGNAL(popPacketData(QByteArray*)),plot,SLOT(setPacketData(QByteArray*)));
        QString info("Press the legend to en/disable a curve    ");
        //QLabel *labelInfo = new QLabel(info, &(*vBox));
        //labelInfo->setFont(QFont("Courier",12,60));
        QHBoxLayout *hLayout = new QHBoxLayout();
        //hLayout->addWidget(labelInfo);
        hLayout->addWidget(labelMin);hLayout->addWidget(minSpinBox);
        hLayout->addWidget(labelMax);hLayout->addWidget(maxSpinBox);hLayout->addStretch();
        QVBoxLayout *vLayout = new QVBoxLayout(&(*vBox));
        vLayout->addWidget(plot);
        vLayout->addLayout(hLayout);
        vBox->showMaximized();

        connect(minSpinBox,SIGNAL(valueChanged(int)),this,SLOT(minAxisScaleChanged(int)));
        connect(maxSpinBox,SIGNAL(valueChanged(int)),this,SLOT(maxAxisScaleChanged(int)));
        connect(vBox,SIGNAL(destroyed()),plotBtn,SLOT(show()));
}


/************************************************************
  下面为plot
  ********************************************************/
class TimeScaleDraw: public QwtScaleDraw
{
public:
    TimeScaleDraw()
    {
    }
    virtual QwtText label(double v) const //时间轴的坐标
    {
        QTime upTime ;
        return upTime.currentTime().toString();
    }
};

class Background: public QwtPlotItem
{
public:
    Background()
    {
        setZ(0.0);
    }

    virtual int rtti() const
    {
        return QwtPlotItem::Rtti_PlotUserItem;
    }

    virtual void draw(QPainter *painter,
        const QwtScaleMap &, const QwtScaleMap &yMap,
        const QRectF &rect) const
    {
        QColor c(Qt::white);
       // c.setAlpha(160);
        QRectF r = rect;

        for ( int i =50; i >=-20; i -= 10 )
        {
            r.setBottom(yMap.transform(i - 10));
            r.setTop(yMap.transform(i));
            painter->fillRect(r, c);
            c = c.darker(120);// 1/1.2 the brightness
        }
        c=Qt::white;//c.setAlpha(160);
        c=c.darker(120);
        for ( int i =60; i <=130; i += 10 )
        {
            r.setBottom(yMap.transform(i - 10));
            r.setTop(yMap.transform(i));
            painter->fillRect(r, c);
            c = c.darker(120);// 1/1.2 the brightness
        }
    }
};

class CpuCurve: public QwtPlotCurve
{
public:
    CpuCurve(const QString &title):
        QwtPlotCurve(title)
    {
        setRenderHint(QwtPlotItem::RenderAntialiased);
    }

    void setColor(const QColor &color)
    {
        QColor c = color; //画笔画刷的颜色
        //c.setAlpha(150); //设置透明度。0~255。 0为透明
        setPen(c);
       // setBrush(c);
    }
};

CpuPlot::CpuPlot(QWidget *parent):
    QwtPlot(parent),
    dataCount(0)
{
    setAutoReplot(false);

    plotLayout()->setAlignCanvasToScales(true);

    QwtLegend *legend = new QwtLegend(); //图例
    legend->setItemMode(QwtLegend::CheckableItem);
    insertLegend(legend, QwtPlot::RightLegend);

    setAxisTitle(QwtPlot::xBottom, " System Time [h:m:s]");
    setAxisScaleDraw(QwtPlot::xBottom,new TimeScaleDraw());
    setAxisScale(QwtPlot::xBottom, 0, HISTORY);

    setAxisLabelRotation(QwtPlot::xBottom, -10.0);
    setAxisLabelAlignment(QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom);

    /*
     In situations, when there is a label at the most right position of the
     scale, additional space is needed to display the overlapping part
     of the label would be taken by reducing the width of scale and canvas.
     To avoid this "jumping canvas" effect, we add a permanent margin.
     We don't need to do the same for the left border, because there
     is enough space for the overlapping label below the left scale.
     */

    QwtScaleWidget *scaleWidget = axisWidget(QwtPlot::xBottom);
    const int fmh = QFontMetrics(scaleWidget->font()).height();//字体高度
    scaleWidget->setMinBorderDist(0, fmh / 2);

    /***下面的语句参考我的日志http://user.qzone.qq.com/892054726/blog/1293351267  ***/
    setAxisTitle(QwtPlot::yLeft, trUtf8("Humanity(%)/Temperature(\342\204\203)"));
    //setAxisTitle(QwtPlot::yLeft,  QApplication::translate("CpuPlot", //作用和上面一句等效
          //  "Humanity(%)/Temperature(\342\204\203)", 0, QApplication::UnicodeUTF8));
    setAxisScale(QwtPlot::yLeft, -20, 100);

    Background *bg = new Background();
    bg->attach(this);

    //CpuPieMarker *pie = new CpuPieMarker();
    //pie->attach(this);

    CpuCurve *curve;

    curve = new CpuCurve("Humi");  //data1
    curve->setColor(Qt::green);
    curve->attach(this);
    data[0].curve = curve;

    curve = new CpuCurve("Temp");  //data2
    curve->setColor(Qt::red);
    curve->setZ(curve->z() - 1);
    curve->attach(this);
    data[1].curve = curve;

    showCurve(data[0].curve, true);
    showCurve(data[1].curve, true);

    for ( int i = 0; i < HISTORY; i++ )
        timeData[HISTORY - 1 - i] = i;// 数组依次存储59~0

    (void)startTimer(500);

    /****************************************
      0.5 second  刷新频率。需要和数据实际的更新速率保持一致。
      或者和读取窗口使用相同的timer(后者估计不太可行）。
*************************************************/

    connect(this, SIGNAL(legendChecked(QwtPlotItem *, bool)),
        SLOT(showCurve(QwtPlotItem *, bool)));
}

void CpuPlot::timerEvent(QTimerEvent *)
{
    /*dataCount是当前窗口中已经显示的数据的列数。由于刚开始是0，然后逐渐增加到
    history，所以在构造函数中，该值被初始化为0
    下面的for语句将图形往前移动
    */
    for ( int i = dataCount; i > 0; i-- )
    {
        for ( int c = 0; c < 2; c++ )
        {
            if ( i < HISTORY )
                data[c].data[i] = data[c].data[i-1];
        }
    }


    /***************************************
      装入一个新数据，以后将该数据逐渐向前移动
      **************************************/
    statistic(data[0].data[0], data[1].data[0]);

    if ( dataCount < HISTORY )
        dataCount++;

    for ( int j = 0; j < HISTORY; j++ )
        timeData[j]++;
    //和CpuPlot的构造函数中相反，这个地方使得timeData中的数依次为0~59

    setAxisScale(QwtPlot::xBottom,timeData[HISTORY - 1], timeData[0]);

    for ( int c = 0; c < 2; c++ )
    {
        data[c].curve->setRawSamples(
            timeData, data[c].data, dataCount);
    }

    replot();
}

void CpuPlot::showCurve(QwtPlotItem *item, bool on)
{
    item->setVisible(on);
    QWidget *w = legend()->find(item);
    if ( w && w->inherits("QwtLegendItem") )
        ((QwtLegendItem *)w)->setChecked(on);

    replot();
}



void CpuPlot::statistic(double &data1, double &data2)
{
    #ifdef random
    QTime time;
    time.start();
    qsrand(time.msec()+time.second());
    data1=qrand()%77;
    data2=qrand()%63;
    #else
    QByteArray dataByteArray;
    bool ok;
    dataByteArray.append(packetData[6]);dataByteArray.append(packetData[7]);
    dataByteArray.append(packetData[4]);dataByteArray.append(packetData[5]);

    quint16 dataQUint16=dataByteArray.toUInt(&ok,16);

    if(packetData[3]=='1') //湿度
    data1=-0.0000028*dataQUint16*dataQUint16
          + 0.0405*dataQUint16-4.0;
    else //温度
      data2=dataQUint16*0.01 - 39.6;
    #endif
}

void CpuPlot::setPacketData(QByteArray *p)
{
    /**************************************
    packetData格式：
    0、1、2、3：sensortype  0001:湿度  0200：温度
    4、5、6、7：data   计算：4/5+6/7*255
         @wulin
    *******************************************/
    if(p->size()>=50&&(*p).at(0)=='7'&&(*p).at(1)=='e')
    {
      quint16 up=38;
      quint16 low=36;
      packetData.clear();
      packetData.append((*p).at(18));packetData.append((*p).at(19));
      packetData.append((*p).at(20));packetData.append((*p).at(21));
      for(int n=0;n<=low-2;n+=2)
      {
          if((*p).at(n)=='7'&&(*p).at(n+1)=='d')
          {low+=2;  //处理数据字节之前的转义
            up+=2;
            }
      }

      for(int n=low;n<=up;n+=2)
      {
            if((*p).at(n)=='7'&&(*p).at(n+1)=='d')
            {  //处理转义字符“7d"
               packetData.append((*p).at(n));
               packetData.append((*p).at(n+3));
               n+=2;
               up+=2;
            }
            else
            {   packetData.append((*p).at(n));
                packetData.append((*p).at(n+1));
            }
        }//end of for
  }//end of if
}

void MainWindow::on_exitBtn_clicked()
{
    QMessageBox msgBox;
    msgBox.setIcon(QMessageBox::Warning);
     msgBox.setText("Do you want to close?");
     msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel );
     msgBox.setDefaultButton(QMessageBox::Cancel);
     int ret = msgBox.exec();
     switch (ret) {
       case QMessageBox::Ok:
           this->close();
           break;
       case QMessageBox::Cancel:
           // Cancel was clicked
           break;
       default:
           // should never be reached
           break;
       }
}
